#!/usr/bin/env perl

use v5.14;
use utf8::all;

use Text::Unidecode;
use Sereal::Encoder;

use constant MAX_ITERATIONS_FOR_MERGE => 5000;

my $encoder = Sereal::Encoder->new;
binmode STDOUT;

my($topic, $body);
my($title_flag, $body_flag) = (1, 0);

#title         => $page_title,
#ascii_title   => unidecode($page_title) =~ s<[^a-zA-Z_-]><_>gr,
#conversations => [
#   {
#     topic       => $topic,
#     ascii_topic => $unidecoded_topic,
#     phrases     => [
#       {
#         text      => $text,
#         signature => $signature,
#         time      => $time,
#         date      => $date,
#         id        => $id,
#         reply_to  => $reply_to_id,
#         indent    => $indent,
#       },
#       ...
#     ],
#   },
#   ...
# ]

my %talkpage;

while(<>) {
    if($_ eq "\0\n") {
        # don't waste time serialzing broken/empty pages
        if(@{ $talkpage{conversations} // [] }) {
            state $ctr = 1;
            $talkpage{id} = $ctr++;
            print $encoder->encode(\%talkpage);
        }

        %talkpage   = ();
        $title_flag = 1;
        $body_flag  = 0;
        $topic = $body = '';
    }
    # FIXME last topic is accumulated but not flushed because there's no more topic markers below
    # factor out this block into a function and call it the block above
    elsif(/^--TOPIC MARKER--(.+?)$/) {
        if($body) {
            # fixup possible incorrect markup (when the very first statement has indent > 0)
            $body =~ s<\A:+><>;

            my @conversations = split /\n{2,}/, $body;

            # merge a conversation that starts
            # with a phrase with indent > 0 into the previous one
            #
            # conversation 1:
            # Hello
            # : Hello2
            #
            # conversation 2:
            # :: Hello3
            # ...
            #
            # should become
            # conversation 1,2:
            # Hello
            # : Hello2
            # :: Hello3
            # ...

            # FIXME sometimes conversations are not split by \n\n
            # check if two utterances in a row have indent = 0
            # if so, split them into two conversations

            my $iter = 0;
            while(1) {
                for my $i (0 .. $#conversations-1) {
                    my $first  = $conversations[$i] or next;
                    my $second = $conversations[$i+1];

                    if($second =~ /^(:+)/) {
                        $conversations[$i]   = join "\n", $first, $second;
                        $conversations[$i+1] = undef;
                    }
                }

                @conversations = grep defined, @conversations;

                last unless grep /^:/, @conversations;
                if(++$iter > MAX_ITERATIONS_FOR_MERGE) {
                    warn "Possible infinite loop while merging conversations ($topic)";
                    last;
                    # XXX exit?
                }
            }

            for my $conversation (@conversations) {
                my @phrases = grep length $_->{text},
                              map +{indent => m<^(:+)> ? length $1 : 0,
                                    id     => state $ctr++,
                                    text   => $_},
                              map s<^\s+|\s+$><>gr,
                              split /\n/,
                              $conversation;

                for my $phrase (@phrases) {
                    ($phrase->{signature}) = $phrase->{text} =~ /--SIGNATURE BEGIN--(.+?)--SIGNATURE END--/;

                    my($datetime) = $phrase->{text} =~ /--DATE BEGIN--(.+?)--DATE END--/;
                    ($phrase->{time}) = $datetime =~ /([0-9]{2}:[0-9]{2}),/;
                    ($phrase->{date}) = $datetime =~ /([0-9]+ [а-яА-Я]+ [0-9]{4} \(UTC\))/;

                    for($phrase->{text}) {
                        s<--(?:SIGNATURE|DATE) (?:BEGIN|END)--><>g;
                        s<^:+><>;
                        s<^\s+|\s+$><>g;
                    }
                }

                # normalize indents (i.e. make them sequential)
                #for my $i (0 .. $#phrases-1) {
                #    my $j = $i + 1;
                #    if($phrases[$j][0] - $phrases[$i][0] > 1) {
                #        $phrases[$j][0] = $phrases[$i][0] + 1;
                #    }
                #}

                # skip main statement
                for my $i (1 .. $#phrases) {
                    my $current = $phrases[$i];

                    # find closest (i.e. closest lower indent value) ancestor
                    for my $j (reverse 0 .. $i) {
                        my $ancestor = $phrases[$j];

                        if($ancestor->{indent} < $current->{indent}) {
                            $current->{reply_to} = $ancestor->{id};
                            last;
                        }
                    }
                }

                push @{ $talkpage{conversations} }, {
                    phrases     => \@phrases,
                    topic       => $topic,
                    #ascii_topic => unidecode($topic) =~ s<[^a-zA-Z0-9_-]><_>gr,
                } if @phrases;
            }

            # prepare for the next conversation
            $body = '';
        }

        $topic = $1;
        $topic =~ s<^\s+|\s+$><>g;

        $body_flag = 1;
    }
    elsif($body_flag) {
        $body .= $_;
    }
    elsif($title_flag) {
        chomp($talkpage{title} = $_);

        $title_flag = 0;
    }
}

# vim: ft=perl
